ffmpeg 将视频帧转换成jpg、png等图片

您所在的位置:网站首页 ffmpeg 图片处理 ffmpeg 将视频帧转换成jpg、png等图片

ffmpeg 将视频帧转换成jpg、png等图片

#ffmpeg 将视频帧转换成jpg、png等图片| 来源: 网络整理| 查看: 265

文章目录 前言一、如何实现?1、查找编码器2、构造编码器上下文3、像素格式转换4、编码5、获取图片数据6、销毁资源 二、完整代码三、使用示例1、视频帧保存jpg文件2、自定义数据构造AVFrame 总结

前言

有时播放实时流的时候有截图的需求,需要将解码出来的图片保存本地或上传服务器,这时就需要将avframe中的数据编码成png、jpg等格式的图片,我们使用ffmpeg的相关编码器就可以实现功能。

一、如何实现? 1、查找编码器

首先需要查找图片编码器,比如jpg为AV_CODEC_ID_MJPEG,png为AV_CODEC_ID_PNG

示例代码:

enum AVCodecID codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); 2、构造编码器上下文

有了编码器就可以构造编码器上下文了。

AVCodecContext*ctx = avcodec_alloc_context3(codec); ctx->bit_rate = 3000000; ctx->width = frame->width;//视频帧的宽 ctx->height = frame->height;//视频帧的高 ctx->time_base.num = 1; ctx->time_base.den = 25; ctx->gop_size = 10; ctx->max_b_frames = 0; ctx->thread_count = 1; ctx->pix_fmt = *codec->pix_fmts;//使用编码器适配的像素格式 //打开编码器 avcodec_open2(ctx, codec, NULL); 3、像素格式转换

如果输入视频帧的像素和编码器的像素格式不相同则需要转换像素格式,我们采用SwsContext 转换即可

AVFrame*rgbFrame = av_frame_alloc();//转换后的帧 swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL); int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2; buffer = (unsigned char*)av_malloc(bufferSize); //构造帧的缓存 av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1); sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize); //构造必要的参数 rgbFrame->format = ctx->pix_fmt; rgbFrame->width = ctx->width; rgbFrame->height = ctx->height; 4、编码

得到转后的帧就可以编码

ret = avcodec_send_frame(ctx, rgbFrame); 5、获取图片数据

获取解码后的包即可得到图片数据。

uint8_t* outbuf;//输出图片的缓存 size_t outbufSize;//缓存大小 AVPacket pkt; av_init_packet(&pkt); //获取解码的包 avcodec_receive_packet(ctx, &pkt); //将图片数据拷贝到缓存 if (pkt.size > 0 && pkt.size av_frame_unref(rgbFrame); av_frame_free(&rgbFrame); } if (buffer) { av_free(buffer); } av_packet_unref(&pkt); if (ctx) { avcodec_close(ctx); avcodec_free_context(&ctx); } 二、完整代码 /// /// 帧转图片 /// 如果外部提供的缓存长度不足则不会写入。 /// /// [in]视频帧 /// [in]图片编码器ID,如jpg:AV_CODEC_ID_MJPEG,png:AV_CODEC_ID_PNG /// [out]图片缓存,由外部提供 /// [in]图片缓存长度 /// 返回图片实际长度 static int frameToImage(AVFrame* frame, enum AVCodecID codecID, uint8_t* outbuf, size_t outbufSize) { int ret = 0; AVPacket pkt; AVCodec* codec; AVCodecContext* ctx = NULL; AVFrame* rgbFrame = NULL; uint8_t* buffer = NULL; struct SwsContext* swsContext = NULL; av_init_packet(&pkt); codec = avcodec_find_encoder(codecID); if (!codec) { printf("avcodec_send_frame error %d", codecID); goto end; } if (!codec->pix_fmts) { printf("unsupport pix format with codec %s", codec->name); goto end; } ctx = avcodec_alloc_context3(codec); ctx->bit_rate = 3000000; ctx->width = frame->width; ctx->height = frame->height; ctx->time_base.num = 1; ctx->time_base.den = 25; ctx->gop_size = 10; ctx->max_b_frames = 0; ctx->thread_count = 1; ctx->pix_fmt = *codec->pix_fmts; ret = avcodec_open2(ctx, codec, NULL); if (ret rgbFrame = av_frame_alloc(); if (rgbFrame == NULL) { printf("av_frame_alloc fail"); goto end; } swsContext = sws_getContext(frame->width, frame->height, (enum AVPixelFormat)frame->format, frame->width, frame->height, ctx->pix_fmt, 1, NULL, NULL, NULL); if (!swsContext) { printf("sws_getContext fail"); goto end; } int bufferSize = av_image_get_buffer_size(ctx->pix_fmt, frame->width, frame->height, 1) * 2; buffer = (unsigned char*)av_malloc(bufferSize); if (buffer == NULL) { printf("buffer alloc fail:%d", bufferSize); goto end; } av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, ctx->pix_fmt, frame->width, frame->height, 1); if ((ret = sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize)) ret = avcodec_send_frame(ctx, frame); } if (ret printf("avcodec_receive_packet error %d", ret); goto end; } if (pkt.size > 0 && pkt.size av_frame_unref(rgbFrame); av_frame_free(&rgbFrame); } if (buffer) { av_free(buffer); } av_packet_unref(&pkt); if (ctx) { avcodec_close(ctx); avcodec_free_context(&ctx); } return ret; } 三、使用示例 1、视频帧保存jpg文件 void main() { AVFrame* frame;//视频解码得到的帧 saveFrameToJpg(frame,"snapshot.jpg"); } /// /// 将视频帧保存为jpg图片 /// /// 视频帧 /// 保存的路径 void saveFrameToJpg(AVFrame*frame,const char*path) { //确保缓冲区长度大于图片,使用brga像素格式计算。如果是bmp或tiff依然可能超出长度,需要加一个头部长度,或直接乘以2。 int bufSize = av_image_get_buffer_size(AV_PIX_FMT_BGRA, frame->width, frame->height, 64); //申请缓冲区 uint8_t* buf = (uint8_t*)av_malloc(bufSize); //将视频帧转换成jpg图片,如果需要png则使用AV_CODEC_ID_PNG int picSize = frameToImage(frame, AV_CODEC_ID_MJPEG, buf, bufSize); //写入文件 auto f = fopen(path, "wb+"); if (f) { fwrite(buf, sizeof(uint8_t), bufSize, f); fclose(f); } //释放缓冲区 av_free(buf); } 2、自定义数据构造AVFrame void main() { uint8_t*frameData;//解码得到的视频数据 AVFrame* frame=allocFrame(frameData,640,360,AV_PIX_FMT_YUV420P); saveFrameToJpg(frame,"snapshot.jpg");//此方法定义在示例1中 av_frame_free(&frame); } /// /// 通过裸数据生成avframe /// /// 帧数据 /// 帧宽 /// 帧高 /// 像素格式 /// avframe,使用完成后需要调用av_frame_free释放 AVFrame* allocFrame(uint8_t*frameData,int width,int height,AVPixelFormat format) { AVFrame* frame = av_frame_alloc(); frame->width = width; frame->height = height; frame->format = format; av_image_fill_arrays(frame->data, frame->linesize, frameData, format, frame->width, frame->height, 64); return frame; } 总结

以上就是今天要讲的内容,总的来说整个流程和一般的视频编码是一致的,只是选择的编码器不同,拿到的图片数据在内存中,可以直接网络传输或保存到本地。可以很方便的在视频界面过程中截图,尤其是解码使用ffmpeg的情况下。实现也不算难,写成文章是为了以后能直接复用,毕竟时间久了一些细节还是会遗忘的。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3